#include "json.hpp"
#include <iostream>
#include <iomanip>
#include <fstream>

struct Asset
{
    std::string name;
    int value;

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Asset, name, value)
};

struct Person
{
    std::string name;
    int age;
    bool married;
    std::vector<std::string> habbits;
    std::vector<Asset> assets;

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Person, name, age, married, habbits, assets)

};

void Test1(void)
{
    Person wb = {"wb",
            18,
            true,
            {"football", "reading"},
            {{"house", 9999}, {"car", 8888}}};

    nlohmann::json j = wb;
    std::cout<< j << std::endl;

    nlohmann::json j2 = j;
    std::cout<< j2 << std::endl;

    //随意添加几个多余的字段
    j2["noused1"] = 1;
    j2["noused2"]["aaa"] = 2;
    j2["noused3"] = {1, 2, 3, 4, 5, 6};
    std::cout<< j2 << std::endl;

    Person wb2 = j2.get<Person>();

    std::cout<< wb2.name << " " << wb2.age << " " << wb2.married << std::endl;
    for (const auto& e : wb2.habbits)
    {
        std::cout<< e << " ";
    }
    std::cout<< std::endl;

    for (const auto& e : wb2.assets)
    {
        std::cout<< e.name << " " << e.value << std::endl;
    }
}

struct TestArray
{
    //nlohmann json支持普通数组，而不像tvm json一样只能用vector/list
    int i_array[10];
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(TestArray, i_array)

void Test2(void)
{
    int i[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    TestArray t;
    memcpy(t.i_array, i, sizeof(t.i_array));

    nlohmann::json j = t;
    std::cout<< j << std::endl;
}

struct student
{
    public:
        std::string name;
        int age;
        std::string phone;
};

struct class_room
{
    public:
        std::string teacher_name;
        std::vector<student> student_list;
};

void from_json(const nlohmann::json& j, student& p)
{
    j.at("name").get_to(p.name);
    j.at("age").get_to(p.age);
    j.at("phone").get_to(p.phone);
}

void to_json(nlohmann::json& j, const student& s)
{
    j = nlohmann::json{ { "name", s.name },{ "age",s.age },{ "phone", s.phone } };
}

void from_json(const nlohmann::json& j, class_room& p)
{
    j.at("teacher_name").get_to(p.teacher_name);
    j.at("student_list").get_to(p.student_list);
}

void to_json(nlohmann::json& j, const class_room& s)
{
    j = nlohmann::json{ {"teacher_name", s.teacher_name }, {"student_list", s.student_list} };
}

void Test3(void)
{
    nlohmann::json j = R"(
{
	"teacher_name": "wangwu",
	"student_list":
	[
		{
		"name": "zhangsan",
		"age": 16,
		"phone": "12345678"
		},
		{
		"name": "lisi",
		"age": 17,
		"phone": "123456"
		}
	]
})"_json;

    class_room cr = j.get<class_room>();
    std::cout<< cr.teacher_name << std::endl;
    for (const auto& e : cr.student_list)
    {
        std::cout<< e.name << " " << e.age << " " << e.phone << std::endl;
    }
}

void Test4(void)
{
    std::string strContent = R"(
{
    "ivalue" : 123,
    "dvalue" : 3.14,
    "strvalue": "Hello, world",
    "arrvalue": [1, 2, 3, 4, 5, 6]
})";
    nlohmann::json j = nlohmann::json::parse(strContent);
    std::cout<< j << std::endl;

    for (nlohmann::json::iterator it = j.begin(); it != j.end(); it++)
    {
        std::string key = it.key();
        if (key == "ivalue")
        {
            int i = it.value();
            std::cout<< i << std::endl;
        }
        else if (key == "dvalue")
        {
            double d = it.value();
            std::cout<< d << std::endl;
        }
        else if (key == "strvalue")
        {
            std::string str = it->get<std::string>();
            std::cout<< str << std::endl;
        }
        else if (key == "arrvalue")
        {
            std::vector<int> arr;
            it->get_to(arr);
            for (auto e : arr)
            {
                std::cout<< e << " ";
            }
            std::cout<< std::endl;
        }
    }

}


struct My
{
    int ivalue = 1234;
    double dvalue = 5.678;

    friend void to_json(nlohmann::json& j, const My& p)
    {
        j = nlohmann::json{{"ivalue", p.ivalue}, {"dvalue", p.dvalue}};
    }
    friend void from_json(const nlohmann::json& j, My& p)
    {
        j["ivalue"].get_to(p.ivalue);
        j["dvalue"].get_to(p.dvalue);
    }
};


void Test5(void)
{
    My m;
    m.ivalue = 22222;
    m.dvalue = 3.3333333;
    nlohmann::json j = m;

    nlohmann::json j2 = j;
    std::cout<< j2 << std::endl;

    My m2 = j2.get<My>();

    std::cout<< m2.ivalue << " " << m2.dvalue << std::endl;
}

/**
 * @brief 示例：在不能确定value对应什么结构的情况下，如何读取value
 *
 */
struct Host
{
    std::string mode;
    int32_t id;

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Host, mode, id)
};
struct Device
{
    std::string mode;
    std::string connect;
    int32_t id;

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Device, mode, connect, id)
};

struct HostOrDevice
{
    Host h;
    Device d;
    int32_t flag;

    friend void to_json(nlohmann::json& j, const HostOrDevice& p)
    {
        if (p.flag == 0)
        {
            j["mode"] = p.h.mode;
            j["id"] = p.h.id;
        }
        else
        {
            j["mode"] = p.d.mode;
            j.emplace("connect", p.d.connect);  //不能用emplace_back
            j.emplace("id", p.d.id);
        }

    }
    friend void from_json(const nlohmann::json& j, HostOrDevice& p)
    {
        for(auto it = j.begin(); it != j.end(); it++)
        {
            std::string key = it.key();

            if (key == "rpc")
            {
                auto& value = it.value();
                if (value.at("mode") == "Host")
                {
                    value.get_to<Host>(p.h);
                    p.flag = 0;
                }
                else if (value.at("mode") == "Device")
                {
                    value.get_to<Device>(p.d);
                    p.flag = 1;
                }
            }
            else
            {
                std::cout<< "ignore key: " << key << std::endl;
            }
        }
    }
};

void Test6(void)
{
    {
    std::string strContent = R"(
{
    "ivalue" : 123,
    "dvalue" : 3.14,
    "rpc" : {
        "mode" : "Host",
        "id" : 100
    },
    "strvalue": "Hello, world",
    "arrvalue": [1, 2, 3, 4, 5, 6]
})";

    nlohmann::json j = nlohmann::json::parse(strContent);
    HostOrDevice hd;
    j.get_to<HostOrDevice>(hd);

    if (hd.flag == 0)
    {
        std::cout<< hd.h.mode << " " << hd.h.id << std::endl;
    }
    else if (hd.flag = 1)
    {
        std::cout<< hd.d.mode << " " << hd.d.connect << " " << hd.d.id << std::endl;
    }

    nlohmann::json jout = hd;
    std::cout<< std::setw(4) << jout << std::endl;

    }

    {
    std::string strContent = R"(
{
    "ivalue" : 123,
    "dvalue" : 3.14,
    "rpc" : {
        "mode" : "Device",
        "connect" : "usb",
        "id" : 200
    },
    "strvalue": "Hello, world",
    "arrvalue": [1, 2, 3, 4, 5, 6]
})";

    nlohmann::json j = nlohmann::json::parse(strContent);
    HostOrDevice hd;
    j.get_to<HostOrDevice>(hd);

    if (hd.flag == 0)
    {
        std::cout<< hd.h.mode << " " << hd.h.id << std::endl;
    }
    else if (hd.flag = 1)
    {
        std::cout<< hd.d.mode << " " << hd.d.connect << " " << hd.d.id << std::endl;
    }

    nlohmann::json jout = hd;
    std::cout<< std::setw(4) << jout << std::endl;
    }
}

struct LogCfg
{
    std::string moduleName;
    std::string logLevel;

    friend void from_json(const nlohmann::json& j, LogCfg& p)
    {
        auto it = j.begin();

        p.moduleName = it.key();
        p.logLevel = it.value();
    }

    friend void to_json(nlohmann::json& j, const LogCfg& p)
    {
        j[p.moduleName] = p.logLevel;
    }
};

struct AllLogCfg
{
    std::vector<LogCfg> log;

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(AllLogCfg, log)
};

void Test7(void)
{
    std::string strContent = R"(
{
    "log": [
        {"dcl": "info"},
        {"dcmi": "info"},
        {"syslink": "info"}
    ]
})";

    nlohmann::json j = nlohmann::json::parse(strContent);
    AllLogCfg cfg;
    j.get_to<AllLogCfg>(cfg);

    std::cout<< "cfg log size: " << cfg.log.size();
    for (const auto& e : cfg.log)
    {
        std::cout<< e.moduleName << ":" << e.logLevel << std::endl;
    }
}

/**
 * @brief 把/proc/cpuinfo中的信息组成json格式字符串
 *
 */
void Test8(void)
{
    nlohmann::ordered_json j;
    std::ifstream ifs("/proc/cpuinfo");
    std::string s;

    int cpu_id = 0;
    while (getline(ifs, s))
    {
        if (s.empty())
        {
            cpu_id++;
        }
        else
        {
            auto delim1 = s.find(':');
            auto delim2 = s.find('\t');
            std::string name;
            if (delim2 == std::string::npos || delim2 > delim1)
            {
                name = s.substr(0, delim1);
            }
            else
            {
                name = s.substr(0, delim2);
            }

            auto delim3 = s.find_first_not_of(" \t", delim1 + 1);
            std::string value;
            if (delim3 != std::string::npos)
            {
                value = s.substr(delim3);
            }

            j["cpu_info"][cpu_id][name] = value;
        }
    }

    std::string sss = j.dump(4);
    std::cout<< sss << std::endl;
}


/**
 * @brief 演示json -> string -> json的用法
 *
 */
void Test9(void)
{
    nlohmann::ordered_json j;
    j["name"] = "zhangsan";

    std::string s = j.dump(4);

    nlohmann::ordered_json k = nlohmann::json::parse(s);
    k["age"] = 123;

    std::cout<< std::setw(4) << k << std::endl;
}

/**
 * @brief 演示遍历json对象的做法
 *
 */
void Test10(void)
{
    std::string strContent = R"(
{
    "ivalue" : 123,
    "dvalue" : 3.14,
    "strvalue": "Hello, world",
    "arrvalue": [1, 2, 3, 4, 5, 6],
    "objvalue": {
        "name": "zhangsan",
        "age": 22,
        "sex": "m"
    }
})";
    nlohmann::ordered_json j = nlohmann::json::parse(strContent);

    //打印各项的类型名和值
    for (auto& it : j)
    {
        std::cout<< it.type_name() << " " << it << std::endl;
    }

    //打印各项的key、value
    //等价于for (auto& it = j.begin(); it != j.end(); ++it)
    for (auto& it : j.items())
    {
        std::cout<< it.key() << " - " << it.value().type_name() << " - " << it.value() << std::endl;
    }

    //遍历某个数组
    auto& arrvalue = j["arrvalue"];
    assert(arrvalue.is_array());
    std::cout<< arrvalue.size() << std::endl;
    for (auto& e : arrvalue)
    {
        assert(e.is_number());
        std::cout<< e << " ";
    }
    std::cout<< std::endl;

    //遍历某个结构体
    auto& objvalue = j["objvalue"];
    assert(objvalue.is_object());
    for (auto it = objvalue.begin(); it != objvalue.end(); ++it)
    {
        std::cout<< it.key() << " - " << it.value().type_name() << " - " << it.value() << std::endl;
    }

    //遍历某个结构体：只取值，不关注key
    for (auto& it : objvalue)
    {
        std::cout<< it.type_name() << " - " << it << std::endl;
    }

}

int main(void)
{
    Test10();
}
